﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Data;

namespace ConvertToExcel
{
    public static class CsvHelper
    {
        /// <summary>
        /// Reads the comma separated values from a stream or file.
        /// Returns the data into an IEnumerable<T> that can be used for LINQ queries.
        /// 
        /// The stream or file will be closed after the last line has been processed.
        /// Because the library implements deferred reading (using Yield Return), this may not happen
        /// for a while.
        /// </summary>
        /// <typeparam name="T">
        /// The records in the returned IEnumerable<T> will be of this type.
        /// </typeparam>
        /// <param name="stream">
        /// The data will be read from this stream.
        /// </param>
        /// <param name="cultureInfo">
        /// If the culture of the input data is different from the current culture,
        /// pass the input culture here. For example, if you are in Australia and the data
        /// is from the US, pass in "en-US". 
        /// </param>
        /// <returns>
        /// Values read from the stream or file.
        /// </returns>
        public static IEnumerable<T> Read<T>(string filePath, CsvParams fileDescription) where T : class, new()
        {
            if (!File.Exists(filePath))
            {
                return null;
            }
            if (IsFileInUse(filePath))
            {
                noticeMessage("info: 目标文件正在被使用");
                return null;
            }
            // Note that ReadData will not be called right away, but when the returned 
            // IEnumerable<T> actually gets accessed.

            return ReadData<T>(filePath, fileDescription);
        }

        public static IEnumerable<T> Read<T>(string filePath) where T : class, new()
        {
            if (!File.Exists(filePath))
            {
                return null;
            }

            if (IsFileInUse(filePath))
            {
                noticeMessage("info: 目标文件正在被使用");
                return null;
            }
            return Read<T>(filePath, new CsvParams(true,',',Encoding.Default,"zh-CN"));
        }

        /// <summary>
        /// Filter Partlist
        /// </summary>
        /// <returns></returns>
        public static IList<Part> Filter(IEnumerable<Part> partlist, string partName, string txtSearch=null)
        {
            if (partlist == null) return null;
            if (!String.IsNullOrEmpty(partName))
            {
                Regex reg = new Regex(partName.Replace("*", "[a-z0-9A-Z]*"));
                var data = partlist.Where(m => reg.IsMatch(m.PartName));
                if (txtSearch != null)
                {

                }
                return data.ToList();
            }
            return partlist.ToList();
        }

        /// <summary>
        /// Read csv file to Datatable
        /// </summary>
        /// <param name="filepath"></param>
        /// <returns></returns>
        public static DataTable CsvToDatatable(string filepath)
        {
            return null;
        }

        /// <summary>
        /// ReadData
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="filePath">
        /// Name of the file associated with the stream.
        /// null if there is no such file.
        /// Used solely when throwing an exception.
        /// </param>
        /// <param name="stream">
        /// All data is read from this stream.
        /// This is a StreamReader rather then a TextReader,
        /// because we need to be able to seek back to the start of the
        /// stream, and you can't do that with a TextReader (or s StringReader).
        /// </param>
        /// <param name="fileDescription"></param>
        /// <returns></returns>
        private static IEnumerable<T> ReadData<T>(string filePath,CsvParams fileDescription) where T : class, new()
        {
            StreamReader stream = new StreamReader(filePath, fileDescription.TextEncoding, fileDescription.DetectEncodingFromByteOrderMarks);

            // If T implements IDataRow, then we're reading raw data rows 
            bool readingRawDataRows = typeof(IDataRow).IsAssignableFrom(typeof(T));

            // The constructor for FieldMapper_Reading will throw an exception if there is something
            // wrong with type T. So invoke that constructor before you open the file, because if there
            // is an exception, the file will not be closed.
            // If T implements IDataRow, there is no need for a FieldMapper, because in that case we're returning
            // raw data rows.
            FieldMapper<T> fm = null;
            if (!readingRawDataRows)
            {
                fm = new FieldMapper<T>(fileDescription, filePath, false);
            }
            CsvStream cs = new CsvStream(stream, null, fileDescription.SeparatorChar);

            // If we're reading raw data rows, instantiate a T so we return objects
            // of the type specified by the caller.
            // Otherwise, instantiate a DataRow, which also implements IDataRow.
            IDataRow row = null;
            if (readingRawDataRows)
            {
                row = new T() as IDataRow;
            }
            else
            {
                row = new DataRow();
            }

            try
            {
                bool firstRow = true;
                while (cs.ReadRow(ref row))
                {
                    // Skip empty lines.
                    // Important. If there is a newline at the end of the last data line, the code
                    // thinks there is an empty line after that last data line.
                    if ((row.Count == 1) &&
                        ((row[0].Value == null) ||
                         (string.IsNullOrEmpty(row[0].Value.Trim()))))
                    {
                        continue;
                    }

                    if (firstRow && fileDescription.ContainFieldName)
                    {
                        if (!readingRawDataRows) { fm.ReadNames(row); }
                    }
                    else
                    {
                        T obj = default(T);
                        try
                        {
                            if (readingRawDataRows)
                            {
                                obj = row as T;
                            }
                            else
                            {
                                obj = fm.ReadObject(row);
                            }
                        }
                        catch (Exception ex)
                        {
                            // Seeing that the AggregatedException was thrown, maximum number of exceptions
                            // must have been reached, so rethrow.
                            // Catch here, so you don't add an AggregatedException to an AggregatedException
                            noticeMessage("error:"+ex.Message);
                            throw ex;
                        }
                        yield return obj;
                    }
                    firstRow = false;
                }
            }
            finally
            {
                stream.Close();
            }
        }

        /// <summary> 
        /// This function checks whether the file is in use or not. 
        /// </summary> 
        /// <param name="filename">File Name</param> 
        /// <returns>Return True if file in use else false</returns> 
        public static bool IsFileInUse(string filename)
        {
            bool locked = false;
            FileStream fs = null;
            try
            {
                fs =
                     File.Open(filename, FileMode.OpenOrCreate,
                     FileAccess.ReadWrite, FileShare.None);
            }
            catch (IOException)
            {
                locked = true;
            }
            finally
            {
                if (fs != null)
                {
                    fs.Close();
                }
            }
            return locked;
        }

        public static NoticeMessage noticeMessage { get; set; }
    }
}
